package adventure;

import adventure.commands.skills.SkillManager;
import adventure.commands.skills.SkillManagerImp;
import adventure.misc.Currency;
import adventure.misc.CurrencyImp;
import adventure.misc.MapGen;
import adventure.misc.MapGenImp;
import adventure.placeable.*;
import adventure.room.RoomManager;
import adventure.room.RoomManagerEdit;
import adventure.room.RoomManagerImp;
import adventure.vocation.VocationManager;
import adventure.vocation.VocationManagerImp;
import colour.ColourUtil;
import help.Help;
import player.Player;
import player.PlayerManager;
import server.util.Broadcaster;
import server.util.BroadcasterImp;

/**
 *
 * @author Michael Hanns
 *
 */
public class AdventureImp implements Adventure, AdventureEdit, Cloneable {

    private String name;
    private double version;
    private double softwareVersion;
    private String description;
    private String author;
    private String loginScreen;
    private RoomManager rooms;
    private PlaceableManager placeables;
    private Currency currency;
    private PlayerManager players;
    private SkillManager skills;
    private VocationManager vocations;
    private boolean playerManagerSet;
    private Broadcaster broadcaster;
    private boolean broadcasterSet;
    private Help advHelp;

    public AdventureImp(String name, double softwareVersion) {
        // Basic info
        this.name = name;
        this.version = 0.01;
        this.softwareVersion = softwareVersion;
        this.description = "";
        this.author = "";
        this.loginScreen = "";

        // Rooms and placeables
        this.rooms = new RoomManagerImp();
        this.placeables = new PlaceableManagerImp();
        this.currency = new CurrencyImp();

        // Players and communications
        this.players = null;
        this.playerManagerSet = false;
        this.broadcasterSet = false;
        this.broadcaster = new BroadcasterImp();


        // Massive chunk of vocations stuff.. note: skills are like Commands and
        // thus need a broadcaster set BEFORE they are initialised to work
        this.vocations = new VocationManagerImp();
        this.skills = new SkillManagerImp(this);
    }

    // Adventure getters/setters
    @Override
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void setVersion(double version) {
        this.version = version;
    }

    @Override
    public void setSoftwareVersion(double version) {
        this.softwareVersion = version;
    }

    @Override
    public void setDescription(String desc) {
        this.description = desc;
    }

    @Override
    public void setAuthor(String auth) {
        this.author = auth;
    }

    @Override
    public void setLoginScreen(String login) {
        this.loginScreen = login;
    }

    @Override
    public void setStartArea(int x, int y, int z) {
        ((RoomManagerEdit) rooms).setStartArea(x, y, z);

        if (playerManagerSet) {
            players.setStartArea(x, y, z);
        }
    }

    @Override
    public void setPlayerManager(PlayerManager p) {
        players = p;
        players.setStartArea(rooms.sx(), rooms.sy(), rooms.sz());
        players.setVocationManager(vocations);
        skills.setPlayerManager(players);
        playerManagerSet = true;
    }

    @Override
    public void setCurrency(Currency currencyName) {
        currency = currencyName;
    }

    @Override
    public void setBroadcaster(Broadcaster b) {
        broadcaster = b;
        broadcasterSet = true;
    }

    @Override
    public void setHelpFile(Help helpFile) {
        advHelp = helpFile;
    }

    @Override
    public boolean ready() {
        if (broadcasterSet && playerManagerSet) {
            return true;
        }
        return false;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public double getVersion() {
        return version;
    }

    @Override
    public double getSoftwareVersion() {
        return softwareVersion;
    }

    @Override
    public String getDescription() {
        return description;
    }

    @Override
    public String getAuthor() {
        return author;
    }

    @Override
    public String getLoginScreen() {
        return loginScreen;
    }

    @Override
    public int sx() {
        return rooms.sx();
    }

    @Override
    public int sy() {
        return rooms.sy();
    }

    @Override
    public int sz() {
        return rooms.sz();
    }

    @Override
    public RoomManagerEdit getRoomManager() {
        return (RoomManagerEdit) rooms;
    }

    @Override
    public PlaceableManagerEdit getPlaceableManager() {
        return (PlaceableManagerEdit) placeables;
    }

    @Override
    public Currency getCurrency() {
        return currency;
    }

    @Override
    public VocationManager getVocationManager() {
        return (VocationManager) vocations;
    }

    @Override
    public SkillManager getSkillManager() {
        return skills;
    }

    @Override
    public PlayerManager getPlayerManager() {
        return players;
    }

    @Override
    public Broadcaster getBroadcaster() {
        return broadcaster;
    }

    @Override
    public void playerQuit(Player p) {
        broadcaster.broadcastLocalToPlayer(p,
                ColourUtil.colourise(p.getName(), ColourUtil.player) + " has quit.");
        if (rooms.roomExists(p.x(), p.y(), p.z())) {
            rooms.leaveRoom(p);
        }
    }

    @Override
    public String tripTriggers(Player activePlayer, String input) {
        String triggers = rooms.tripTriggers(activePlayer, input);

        return triggers;
    }

    @Override
    public String tripTriggers(Player activePlayer) {
        String triggers = rooms.tripTriggers(activePlayer, null);

        return triggers;
    }

    @Override
    public String printRoom(Player activePlayer) {
        int x = activePlayer.x();
        int y = activePlayer.y();
        int z = activePlayer.z();

        String output = ColourUtil.colourise(rooms.getRoomTitle(x, y, z), ColourUtil.roomTitle);
        if (activePlayer.isAdmin()) {
            output += " (" + x + ", " + y + ", " + z + ")";
        }
        output += ColourUtil.colourise("\n------------\n", ColourUtil.white);
        output += ColourUtil.colourise(rooms.getRoomDescription(x, y, z), ColourUtil.roomDescription) + "\n\n";
        output += rooms.getRoomYouSee(x, y, z, activePlayer.getID()) + "\n";
        output += ColourUtil.colourise("Available paths:\n", ColourUtil.white)
                + ColourUtil.colourise(rooms.getPaths(x, y, z), ColourUtil.grey);

        return output;
    }

    @Override
    public synchronized void aggroMobsAttack(Player p) {
        String aggroOut = rooms.aggroMobsAttack(p);
        if (aggroOut.length() > 0) {
            broadcaster.broadcastToPlayer(p, aggroOut);
        }
    }

    @Override
    public synchronized void aggroGroupsAttack() {
        Player p;
        String[][] aggroOut;
        for (int x = 0; x < players.onlinePlayers(); x++) {
            p = players.getOnlinePlayerAtIndex(x);
            aggroOut = rooms.aggroGroupsAttack(p);

            if (aggroOut != null) {
                for (int y = 0; y < aggroOut.length; y++) {
                    if (aggroOut[y][1] != null) {
                        broadcaster.broadcastToPlayer(players.getOnlinePlayer(
                                aggroOut[y][0]), aggroOut[y][1]);
                    }
                }
            }
        }
    }

    @Override
    public Mobile[] getAggroMobs(Player p) {
        return rooms.getAggroMobs(p.getID(), p.x(), p.y(), p.z());
    }

    @Override
    public String help(String query) {
        return advHelp.query(query);
    }

    @Override
    public String respawnPlayer(Player player) {
        Item[] playerInven = player.getAllItems();

        // Build the corpse
        int mobNo = 1;
        for (; placeables.getMobile(mobNo).getID() > 0; mobNo++) {
        }

        MobileEdit corpse = new MobileImp(mobNo);
        corpse.setName(player.getAliveName());
        corpse.setDescription("It's the rotting remains of what was once " + player.getAliveName() + ".");
        corpse.setDead(true);
        corpse.setCurrencyAmount(player.getCurrencyAmount());
        for (int c = 0; c < playerInven.length; c++) {
            corpse.addInventoryItem(playerInven[c]);
        }
        ((PlaceableManagerEdit) placeables).newMobile(corpse);

        ((RoomManagerEdit) rooms).addMobileToRoom(corpse, player.x(), player.y(), player.z());

        broadcaster.broadcastLocalToPlayer(player, player.getAliveName()
                + "'s soul has left their body!");

        rooms.leaveRoom(player);
        player.respawn();
        rooms.enterRoom(player);
        broadcaster.broadcastLocalToPlayer(player, player.getName() + " has respawned!");

        return "You have respawned!\n\n" + printRoom(player);
    }
    
    @Override
    public Adventure cloneThis() {
        Adventure newClone = new AdventureImp("", 0);
        try {
            newClone = (Adventure) this.clone();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return newClone;
    }

    @Override
    public Object clone() {
        try {
            Object clone = super.clone();
            ((AdventureImp)clone).placeables = this.placeables;
            ((AdventureImp)clone).skills = this.skills;
            ((AdventureImp)clone).currency = this.currency;
            ((AdventureImp)clone).vocations = this.vocations;
            ((AdventureImp)clone).rooms = ((RoomManagerEdit)this.rooms).cloneThis();

            return clone;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
}
